关于ReentrantLock

从字面意思可以看出,ReentrantLock是可重入锁的意思,当然,其实Synchronized也是可重入锁,两者都是同一个线程每进入一次,锁的计算器都自增1,所以要等到锁的计数器为0时才能释放锁。

ReentrantLock最常见的使用方式

1
2
3
4
5
6
7
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
... // 业务逻辑
} finally {
lock.unlock();
}

正如上面这段代码所示,在使用ReentrantLock时需要手动去加锁和解锁,这是因为ReentrantLock是基于JDK实现的,而我们最常使用的锁Synchronized是依赖于JVM来实现的,由JVM帮我们完成解锁操作。

ReentrantLock代码分析

阅读源码最好的方法就是从某个常用方法进入,比如我们在使用ReentrantLock时最常使用的就是其lock()方法,我们看下ReentrantLock的lock()方法:

1
2
3
public void lock() {
sync.lock();
}

lock()方法里的代码很简短,这里出现了一个变量sync,那么它是什么意思呢?在ReentrantLock中其定义为:

1
private final Sync sync;

其为一个Sync类型的常量,我们继续看下Sync类是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
...
}

Sync是ReentrantLock的静态抽象内部类,其继承自AbstractQueuedSynchronizer,AbstractQueuedSynchronizer也就是AQS,我们现在不去分析它,只需要知道其内部存在一个获取锁的等待队列及其互斥锁状态的int状态位(0表示当前没有线程持有该锁,n表示存在某线程重入锁n次)。

正如上面所述,Sync是抽象类,在ReentrantLock中其具有两个实现类,分别为NonfairSync和FairSync,也就是非公平锁和公平锁。

ReentrantLock的lock()方法调用的是Sync类的lock()方法,而Sync类的lock()方法是一个抽象方法,其具体实现在NonfairSync和FairSync中,在NonfairSync类中lock()方法的实现为:

1
2
3
4
5
6
7
8
final void lock() {
// 利用CAS置状态位,如果成功,则表示占有锁成功
if (compareAndSetState(0, 1))
// 记录当前线程为锁拥有者
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

而在FairSync类中lock()方法的实现为:

1
2
3
final void lock() {
acquire(1);
}

可见两者的lock()方法中都涉及到了一个acquire()函数,而acquire()方法的具体实现在Sync类的父类也即AbstractQueuedSynchronizer中:

1
2
3
4
5
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

通过tryAcquire()方法试图获取锁,获取到直接返回结果,否则通过嵌套调用acquireQueued()和addWaiter()方法将请求获取锁的线程加入等待队列。

tryAcquire()方法在AbstractQueuedSynchronizer抽象类中的实现为:

1
2
3
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}

发现在AbstractQueuedSynchronizer类的tryAcquire()方法中直接抛出了异常,事实上tryAcquire()方法在FairSync和NonfairSync两个实现类中均有实现,我们看下NonfairSync类中的tryAcquire()方法吧:

1
2
3
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}

发现在NonfairSync类中的tryAcquire()方法中直接调用了nonfairTryAcquire()方法,而这个方法在其父类即Sync类中已经实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获得状态
int c = getState();
// 状态为0,表示锁未被其他线程占用
if (c == 0) {
// 此时再次利用CAS去尝试占有锁
if (compareAndSetState(0, acquires)) {
// 标记当前线程为锁拥有者
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程已经占有了,则state + 1,记录占有次数
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

上来先判断锁的状态,若为0,则表示锁还未被占用,则直接通过CAS来抢占,抢占成功,返回true,反之,若不为0,则表示锁已经被占用,此时判断锁的持有者线程是否为当前线程,若是,则通过累加状态标识重入次数。

抢占不成功或者锁的本身持有者不是当前线程,则返回false,继而后续通过进入等待队列的方式排队获取锁。

至于FairSync类中的tryAcquire()这里就不加以介绍了,感兴趣的可以自己去阅读以下,其实差不多。

默认实现

ReentrantLock的默认实现为非公平锁:

1
2
3
public ReentrantLock() {
sync = new NonfairSync();
}

你也可以通过另外一个构造方法执行锁的实现方式:

1
2
3
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

说明

本文参考了文章Java并发之ReentrantLock详解